package mymyoux.utils
{
	import flash.net.registerClassAlias;
	import flash.utils.Dictionary;
	import mymyoux.signals.Signal;
	registerClassAlias( "mymyoux.utils.Hash", Hash );
	/**
	 * Hash: links a key string to a value object
	 */
	public class Hash implements ISerializable
	{
		/**
		 * @private
		 */
		protected var _signal:Signal;
		/**
		 * @private
		 */
		protected var _dic:Dictionary;
		/**
		 * @private
		 */
		protected var _keys:Vector.<String>;
		/**
		 * Create an Hash object.
		 * @param data Hash, Dictionnary or Object to fill the new Hash with
		 */ 
		public function Hash(data:Object = null)
		{
			_signal = new Signal(String, Object, Object);
			_dic = new Dictionary(true);
			_keys = new Vector.<String>();
			merge(data);
		}
		/**
		 * Erase all recorded datas
		 */
		public function clear():void
		{
			var p:String;
			var k:Vector.<String> = vKeys;
			for(p in k)
			{
				remove(k[p]);
			}
		}
		/**
		 * Return a new Hash with same records
		 */
		public function clone():Hash
		{
			return new Hash(this);
		}
		/**
		 * Test the existence of a record
		 * @return True if the keys is linked to a value. False otherwise 
		 */
		public function exists(key:String):Boolean
		{
			return has(key);
		}
		/**
		 * Get value linked to the key
		 * @param key Key
		 * @return Value linked to the key or null if no value exists
		 */
		public function get(key:String):Object
		{
			return _dic[key];
		}
		/**
		 * @copy #exists()
		 */
		public function has(key:String):Boolean
		{
			return _dic[key] != undefined && _dic[key] != null;
		}
		/**
		 * The array of existing keys
		 */
		public function get keys():Array
		{
			var k:Array = new Array();
			var p:String;
			for(p in _keys)
			{
				k.push(_keys[p]);
			}
			return k;
		}
		/**
		 * @copy size()
		 */
		public function get length():uint
		{
			return _keys.length;
		}
		/**
		 * Clear all existing records and load all records of the Hash, Dictionary or Object given
		 * @param data Hash, Dictionary or Object
		 */
		public function load(data:Object):void
		{
			if(data !== this)
			{
				clear();
				merge(data);
			}
		}
		/**
		 * Load all records of the Hash, Dictionary or Object given but don't clear previous records
		 * @param data Hash, Dictionary or Object
		 */
		public function merge(data:Object):void
		{
			if(data != null)
			{
				var p:String;
				if(data is Hash)
				{
					var k:Vector.<String> = data.vKeys;
					for(p in k)
					{
						set(k[p], data.get(k[p]));
					}
				}
				else
				{
					
					if(data is Dictionary)
					{
						for(p in data)
						{
							set(p, data[p]);
						}
					}else
					{
						try
						{
							for(p in data)
							{
								set(p, data[p]);
							}	
						}catch(error:Error)
						{
							
						}
					}
				}
			}
		}
		/**
		 * @copy set()
		 */
		public function put(key:String, value:Object):void
		{
			set(key, value);
		}
		/**
		 * Erase the record linked by the given key
		 * @param key Key
		 */
		public function remove(key:String):void
		{
			if(has(key))
			{
				var old:Object = _dic[key];
				_keys.splice(_keys.indexOf(key), 1);
				delete _dic[key];
				_signal.dispatch(key, null, old);
			}
		}
		/**
		 * @inheritDoc
		 */
		public function get serialized():Object
		{
			var p:String;
			var k:Object = new Object();
			for(p in _keys)
			{
				k[_keys[p]] = _dic[_keys[p]];
			}
			return k;
		}
		public function set serialized(value:Object):void
		{
			load(value);
		}
		/**
		 * Save a new record to the Hash
		 * @param key Key linked to the value
		 * @param value Value
		 */
		public function set(key:String, value:Object):void
		{
			if(value !== _dic[key])
			{
				if(value == null)
				{
					remove(key);
					return;
				}
				var old:Object = _dic[key];
				_dic[key] = value;
				if(_keys.indexOf(key) == -1)
				{
					_keys.push(key);
				}
				_signal.dispatch(key, value, old);
			}
		}
		/**
		 * This signal dispatches an event when a value is added, modified or removed.
		 * signal(key:String, newValue:Object, oldValue:Object);
		 */
		public function get signal():Signal
		{
			return _signal;
		}
		/**
		 * Number of records
		 */
		public function size():uint
		{
			return length;
		}
		/**
		 * @inheritDoc
		 */
		public function toString():String
		{
			var s:String = "[";
			if(length == 0)
			{
				return s+"]";
			}
			var p:String;
			for(p in _keys)
			{
				s+="{"+_keys[p]+":"+_dic[_keys[p]]+"},";	
			}
			return s.substring(0, s.length - 1) +"]";
		}
		/**
		 * The Vector.&lt;String&gt; of existing keys
		 */
		public function get vKeys():Vector.<String>
		{
			return _keys.concat();
		}
	}
}